package com.yuanmei.datas;

import android.util.Log;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.LinkedBlockingDeque;

import io.realm.Realm;
import io.realm.RealmConfiguration;

import com.yuanmei.datas.FetchDataService.EmKLineType;
import com.yuanmei.model.StockItem;
import com.yuanmei.common.Utils;

public class FetchKLineDaemon {
    private static final String TAG = FetchKLineDaemon.class.getSimpleName();

    private LinkedBlockingDeque<DownloadInfo> mPendingStocks;
    private Thread mDownloadThread;
    private KLineListener mDownloadListener;
    private boolean mDownloadThreadRunning = true;

    private class DownloadInfo {
        private String mNetEaseCode;
        private boolean mIsIndex;
        private EmKLineType mKLineType;

        private DownloadInfo(String neCode, boolean isIndex, EmKLineType type) {
            mNetEaseCode = neCode;
            mIsIndex = isIndex;
            mKLineType = type;
        }
    }

    // 数据监听接口
    public interface KLineListener {
        void onLineFetched(String neCode, Realm realm, EmKLineType type);
    }

    private void updateDayLine(DownloadInfo info) {
        String neCode = info.mNetEaseCode;
        RealmConfiguration stockRealmConfig = new RealmConfiguration.Builder()
                .name(Utils.getDayRealmFile(neCode))
                .deleteRealmIfMigrationNeeded()
                .build();
        Realm realm = Realm.getInstance(stockRealmConfig);
        Date lastCloseDate  = Utils.geLastCloseDate();
        int updateCount = 0;
        if (info.mIsIndex) {
            updateCount = TenCentWebSource.updateHistoryDayLine(neCode, realm, lastCloseDate);
        } else {
            // 网易失效了
//            NetEaseWebSource.updateHistoryDayLine(neCode, realm, lastCloseDate);
            updateCount = TenCentWebSource.updateHistoryDayLine(neCode, realm, lastCloseDate);
        }
        if (mDownloadListener != null) {
            mDownloadListener.onLineFetched(neCode, realm, EmKLineType.DAY_LINE);
        }
        realm.close();
        if (!Realm.compactRealm(stockRealmConfig)) {
            Log.w(TAG, "Update " + neCode + "'s day line over, but compact db_file fail");
        }
        if (updateCount < 0)
            Log.w(TAG, "Update " + neCode + "'s dayLine to " +
                    Utils.sHyphenDateFormat.format(lastCloseDate) +" fail, count=0");
    }

    private void updateWeekLine(DownloadInfo info) {
        String neCode = info.mNetEaseCode;
        Log.d(TAG, "Start update " + neCode + "'s week line");
        RealmConfiguration config = new RealmConfiguration.Builder()
                .name(Utils.getWeekRealmFile(neCode))
                .deleteRealmIfMigrationNeeded()
                .build();
        Realm realm = Realm.getInstance(config);
        Date end = Utils.getLastTradeDate();
        Calendar cal = Calendar.getInstance(Locale.CHINA);
        cal.setTime(end);
        // Sunday - Saturday: from 1 - 7 -> 0 - 6
        int dayOfWeekISO = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (dayOfWeekISO > 0 && dayOfWeekISO < 6) {
            // 周线截止日期回退取整
            end.setTime(end.getTime() - dayOfWeekISO * Utils.MILLIS_PER_DAY);
        }
        TenCentWebSource.updateHistoryWeekQuotes(neCode, realm, end);
        if (mDownloadListener != null) {
            mDownloadListener.onLineFetched(neCode, realm, EmKLineType.WEEK_LINE);
        }
        realm.close();
        if (!Realm.compactRealm(config)) {
            Log.w(TAG, "After TT update week_line, compact realm fail");
        }
    }

    private void updateMonthLine(DownloadInfo info) {
        String neCode = info.mNetEaseCode;
        Log.d(TAG, "Start update " + neCode + "'s month line");
        RealmConfiguration config = new RealmConfiguration.Builder()
                .name(Utils.getMonthRealmFile(neCode))
                .deleteRealmIfMigrationNeeded()
                .build();
        Realm realm = Realm.getInstance(config);

        Date end = Utils.getLastTradeDate();
        Calendar cal = Calendar.getInstance(Locale.CHINA);
        cal.setTime(end);
        /*
         * DAY_OF_MONTH 从 1 开始
         * 结束日期在月初 如 2019-08-01 ，Tencent 返回的最后月线还是 2019-07-31，所以可以少减1
         */
        int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH) - 1;
        if (dayOfMonth > 0) {
            end.setTime(end.getTime() - dayOfMonth * Utils.MILLIS_PER_DAY);
        }
        TenCentWebSource.updateHistoryMonthQuotes(neCode, realm, end);
        if (mDownloadListener != null) {
            mDownloadListener.onLineFetched(neCode, realm, EmKLineType.MONTH_LINE);
        }
        realm.close();
        if (!Realm.compactRealm(config)) {
            Log.w(TAG, "After TT update month_line, compact realm fail");
        }
    }

    private void updateHourLine(DownloadInfo info) {
        String neCode = info.mNetEaseCode;
        Log.d(TAG, "Start update " + neCode + "'s hour line");
        RealmConfiguration config = new RealmConfiguration.Builder()
                .name(Utils.getHourRealmFile(neCode))
                .deleteRealmIfMigrationNeeded()
                .build();
        Realm realm = Realm.getInstance(config);
        Date end = Utils.getLastTradeDate();

        TenCentWebSource.updateHistoryHourQuotes(neCode, realm, end);
        if (mDownloadListener != null) {
            mDownloadListener.onLineFetched(neCode, realm, EmKLineType.HOUR_LINE);
        }
        realm.close();
        if (!Realm.compactRealm(config)) {
            Log.w(TAG, "After TT update hour_line, compact realm fail");
        }
    }

    // .directory(getRealmDir()) 不能用 sdcard 目录，用了会有稀奇古怪的退出（JNI LIBC abort异常）
    private class DownloadTask implements Runnable {
        @Override
        public void run() {
            while (mDownloadThreadRunning) {
                DownloadInfo info = null;
                try {
                    info = mPendingStocks.takeFirst();
                    if (info.mKLineType == EmKLineType.DAY_LINE) {
                        updateDayLine(info);
                    } else if (info.mKLineType == EmKLineType.WEEK_LINE) {
                        updateWeekLine(info);
                    } else if (info.mKLineType == EmKLineType.HOUR_LINE) {
                        updateHourLine(info);
                    } else if (info.mKLineType == EmKLineType.MONTH_LINE) {
                        updateMonthLine(info);
                    }
                } catch (InterruptedException e) {
                    break;
                } catch (Exception e) {
                    Log.e(TAG, "Unknown exception do update stock:"
                            + (info == null ? "Null" : "" + info.mNetEaseCode), e);
                }
            }
        }
    }

    public FetchKLineDaemon() {
        mPendingStocks = new LinkedBlockingDeque<>();
        mDownloadThread = new Thread(new DownloadTask(), "BGDown");
        mDownloadThread.start();
    }

    public void startPriorUpdate(String neCode, boolean isIndex, EmKLineType type, KLineListener listener) {
        mDownloadListener = listener;
        mPendingStocks.addFirst(new DownloadInfo(neCode, isIndex, type));
        Log.d(TAG, "Start prior update " + neCode + " " + type.name());
    }

    public void scheduleNormalUpdate(List<StockItem> updateList) {
        for (StockItem item : updateList) {
            mPendingStocks.addLast(
                    new DownloadInfo(item.netEaseCode(),
                            item.isMarketIndex(),
                            EmKLineType.DAY_LINE));
        }
    }

    public void stop() {
        mDownloadThreadRunning = false;
        mDownloadThread.interrupt();
    }

}